<!DOCTYPE html>
<html>
<head>
    <meta charset="UTF-8">
    <title>航班监测-Elasticsearch服务示例</title>
    <link rel="stylesheet" href="http://cdn.bootcss.com/bootstrap/3.3.7/css/bootstrap.min.css">
    <link rel="stylesheet" href="http://cdn.bootcss.com/bootstrap-datetimepicker/4.17.47/css/bootstrap-datetimepicker.min.css">
    <link rel="stylesheet" href="http://cdn.bootcss.com/leaflet/1.0.3/leaflet.css">
    <link rel="stylesheet" href="http://cdn.bootcss.com/bootstrap-select/1.12.2/css/bootstrap-select.min.css">
    <style>
        .bootstrap-select {
            width: 168px !important;
        }

        .form-group label {
            width: 90px;
        }

        .tips {
            z-index: 1000;
        }
    </style>
</head>
<body style=" margin: 0;overflow: hidden;background: #fff;width: 100%;height:100%;position: absolute;top: 0;">
<div id="map" style="margin:0 auto;width: 100%;height: 100%;border: 1px solid #dddddd"></div>
<script type="text/javascript" src="http://cdn.bootcss.com/jquery/3.1.1/jquery.min.js"></script>
<script type="text/javascript" src="http://cdn.bootcss.com/bootstrap/3.3.7/js/bootstrap.min.js"></script>
<script type="text/javascript" src="http://cdn.bootcss.com/moment.js/2.18.1/moment.min.js"></script>
<script type="text/javascript" src="http://cdn.bootcss.com/bootstrap-datetimepicker/4.17.47/js/bootstrap-datetimepicker.min.js"></script>
<script type="text/javascript" src="http://cdn.bootcss.com/moment.js/2.18.1/locale/zh-cn.js"></script>
<script type="text/javascript" src="http://cdn.bootcss.com/bootstrap-select/1.12.2/js/bootstrap-select.min.js"></script>
<script type="text/javascript" src="http://mapv.baidu.com/build/mapv.js"></script>
<script type="text/javascript" src="http://cdn.bootcss.com/leaflet/1.0.3/leaflet.js"></script>
<script type="text/javascript" src="http://cdn.bootcss.com/elasticsearch/13.0.1/elasticsearch.min.js"></script>
<script type="text/javascript" src="http://cdn.bootcss.com/randomcolor/0.5.2/randomColor.min.js"></script>
<script type="text/javascript" src="../../dist/iclient9-leaflet.min.js"></script>
<script type="text/javascript">
    var map;
    var timeControl,
        liveESService,//实时点查询的ESService
        trackESService,//历史轨迹查询的ESService
        liveRenderer,//实时点层
        trackRenderer,//历史轨迹层
        liveDataSet,//渲染用实时点数据集
        trackDataSet,//渲染用历史轨迹线数据集
        planeStyles,
        planeLabels,//航班信息提示框集合
        geoFenceLabels,//地理围栏信息提示框集合
        geoFenceLayer,
        dataUrl = "192.168.13.192:9200";

    var flightIdens = [
        'CCA4189', 'CCA4187', 'CBJ5531', 'CSC8747', 'TBA9879',
        'CCA4113', 'CES2251', 'CCA4521', 'CSC8927', 'CSC8811',
        'CSC8857', 'UEA2711', 'UEA2205', 'CCA4439', 'CCA415',
        'CSC8719', 'CSC8555', 'CCA4211', 'HDA825'
    ];

    init();

    function init() {
        initMap();
        liveESService = new SuperMap.ElasticSearchService(dataUrl, {
            change: renderLive,
            outOfGeoFence: renderOutOfGeoFenceLabel
        });
        trackESService = new SuperMap.ElasticSearchService(dataUrl, {
            change: renderTrack
        });
        planeStyles = initPlaneStyles();
    }


    //开始播放
    function start() {
        var options = getControlOptions();
        setGeoFence();
        if (!timeControl) {
            timeControl = new SuperMap.TimeFlowControl(loadLiveData, options);
        } else {
            timeControl.updateOptions(options);
        }
        timeControl.start();
    }

    //暂停播放
    function pause() {
        timeControl.pause();
    }

    //停止播放
    function stop() {
        timeControl.stop();
        clearAll();
    }

    //时间控制器回调参数，即每次刷新时执行的操作，此处为向服务器请求数据并绘制。实时刷新执行。
    function loadLiveData(currentTime) {
        //第一次请求之前先查询轨迹
        if (currentTime === timeControl.getStartTime()) {
            loadTrackData();
        }

        var datetime = moment(currentTime).format("YYYY-MM-DD HH:mm:ss");
        var options = getServiceParamOptions();
        var liveParameters = [];
        for (var i = 0; i < options.flightIds.length; i++) {
            var code = options.flightIds[i];
            if (code === 'ZUUU') {
                continue;
            }
            var sParam = new SuperMap.MatchParameter('ident', code);
            var filterParam = new SuperMap.MatchParameter('datetime', datetime);
            var params = new SuperMap.ElasticSearchParameter({
                index: "aliveflight",
                type: "flight",
                sort: {datetime: {order: "ASC"}},
                must: sParam,
                filter: filterParam
            });
            liveParameters.push(params);
        }
        liveESService.msearch(liveParameters);
        updateProgress(datetime);
    }

    //第一次请求先查询历史轨迹数据，只调用一次
    function loadTrackData() {
        var start = timeControl.getStartTime();
        var end = timeControl.getEndTime();
        var startTime = moment(start).format("YYYY-MM-DD HH:mm:ss");
        var endTime = moment(end).format("YYYY-MM-DD HH:mm:ss");
        var options = getServiceParamOptions();
        var trackParameters = [];
        for (var i = 0; i < options.flightIds.length; i++) {
            var code = options.flightIds[i];
            if (code === 'ZUUU') {
                continue;
            }
            var sParam = new SuperMap.MatchParameter('ident', code);
            var filterParam = new SuperMap.RangeParameter("datetime", null, startTime, null, endTime);
            var params = new SuperMap.ElasticSearchParameter({
                index: "aliveflight",
                type: "flight",
                sort: {datetime: {order: "ASC"}},
                size: 10000,
                must: sParam,
                filter: filterParam
            });
            trackParameters.push(params);
        }
        trackESService.msearch(trackParameters);
    }


    //渲染轨迹图层
    function renderTrack(result) {
        result = result || {};
        var data = createTrackRendererData(result);
        if (data.length < 1) {
            return;
        }
        trackDataSet = new mapv.DataSet(data);
        var options = {
            zIndex: 1,
            strokeStyle: 'rgba(255, 250, 50, 0.8)',
            shadowColor: 'rgba(255, 250, 50, 1)',
            shadowBlur: 1.5,
            lineWidth: 1,
            draw: 'simple'
        };
        trackRenderer = L.supermap.mapVLayer(trackDataSet, options).addTo(map);
    }

    //解析轨迹查询结果数据为mapv数据
    function createTrackRendererData(results) {
        var data = [];
        results.map(function (result) {
            var coordinates = [];
            var ident;
            result.hits.hits.map(function (source) {
                content = source._source;
                if (!ident) {
                    ident = content.ident;
                }
                coordinates.push([content.x, content.y]);
            });
            data.push({
                geometry: {
                    type: 'LineString',
                    coordinates: coordinates
                }
            });
        });
        return data;
    }

    //渲染实时点数据
    function renderLive(result) {
        if (timeControl && !timeControl.getRunning()) {
            return;
        }
        result = result || {};
        var data = createLiveRendererData(result);
        if (data.length < 1) {
            return;
        }
        updateDataSet(data);


        if (!liveRenderer) {
            liveRenderer = L.supermap.mapVLayer(liveDataSet, {
                zIndex: 800,
                draw: 'icon'
            }).addTo(map);
        } else {
            liveRenderer.update(liveDataSet);
        }
        renderLiveLabel(data);
    }

    //解析点查询结果数据为mapv数据
    function createLiveRendererData(results) {
        var data = [];
        results.map(function (result) {
            result.hits.hits.map(function (source) {
                var content = source._source;
                data.push({
                    geometry: {
                        type: 'Point',
                        coordinates: [content.x, content.y],

                    },
                    deg: -content.direction,
                    icon: planeStyles[content.ident].img,
                    ident: content.ident,
                    content: content
                });
            });
        });
        return data;
    }

    //更新点数据集
    function updateDataSet(data) {
        if (!liveDataSet) {
            liveDataSet = new mapv.DataSet(data);
            return;
        }
        var innerData = liveDataSet.get();
        var dataLen = data.length;
        for (var i = 0; i < innerData.length; i++) {
            if (i < dataLen && data[i].ident === innerData[i].ident) {
                innerData[i] = data[i];
            }
        }
        liveDataSet.set(innerData);
    }

    //更新实时点的提示框
    function renderLiveLabel(data) {
        planeLabels = planeLabels || {};
        for (var i = 0; i < data.length; i++) {
            var content = data[i].content;
            var latlng = L.latLng(content.y, content.x);
            var labelContent = "航班： " + content.ident + "<br>" +
                "始发地： " + content.origin + "/" + content.originLabel + "<br>" +
                "目的地： " + content.destination + "/" + content.destinationLabel + "<br>" +
                "时间： " + content.datetime;
            if (!planeLabels[content.ident]) {
                planeLabels[content.ident] = L.popup({
                    closeOnClick: false,
                    autoPan: false,
                    maxWidth: 180,
                    offset: L.point(0, -1)
                }).setContent(labelContent).setLatLng(latlng).addTo(map);
            } else {
                planeLabels[content.ident].setLatLng(latlng).setContent(labelContent);
            }
        }
    }

    //更新地理围栏提示框
    function renderOutOfGeoFenceLabel(data) {
        geoFenceLabels = geoFenceLabels || {};
        var content = data.hits.hits[0]._source;
        var latlng = L.latLng(content.y, content.x);
        var labelContent = "<div style='color:red'>航班" + content.ident + "超出地理围栏</div>";
        if (!geoFenceLabels[content.ident]) {
            geoFenceLabels[content.ident] = L.popup({
                closeOnClick: false,
                autoPan: false,
                maxWidth: 180,
                className: "tips",
                offset: L.point(0, -1)
            }).setContent(labelContent).setLatLng(latlng).addTo(map);
        } else {
            geoFenceLabels[content.ident].setLatLng(latlng).setContent(labelContent).openOn(map);
        }
    }


    //点击开始之后配置地理围栏
    function setGeoFence() {
        var geoFence = getGeoFence();
        liveESService.setGeoFence(geoFence);
        if (!geoFenceLayer) {
            geoFenceLayer = L.circle(L.latLng(geoFence.center[1], geoFence.center[0]), {
                radius: geoFence.radius,
                weight: 1
            }).addTo(map);
        }
    }

    //初始化飞机的样式，颜色随机
    function initPlaneStyles() {
        var planes = {};
        var colors = randomColor({
            luminosity: 'bright',
            hue: 'random',
            alpha: 1,
            count: flightIdens.length
        });
        flightIdens.map(function (ident, key) {
            var svgUrl = "data:image/svg+xml;utf8,";
            $.get('../data/plane.svg', function (svg) {
                $(svg).children('svg').children('path').attr('fill', colors[key]);
                svgUrl += $(svg).children('svg')[0].outerHTML;
                var img = new Image();
                img.src = svgUrl;
                planes[ident] = {color: colors[key], img: img};
            });
        });
        return planes;
    }


    //获取时间控件设置的参数
    function getControlOptions() {
        var startTime = $("#startTime").val();
        var endTime = $("#endTime").val();
        startTime = Date.parse(new Date(startTime));
        endTime = Date.parse(new Date(endTime));
        var speed = $("#speed").val();
        speed = (speed > 0) ? speed : 1000;
        speed = parseInt(speed);
        var frequency = $("#frequency").val();
        frequency = (frequency > 0) ? frequency : 1000;
        frequency = parseInt(frequency);
        return {
            startTime: startTime,
            endTime: endTime,
            speed: speed,
            frequency: frequency
        }
    }

    //获取ES服务查询参数
    function getServiceParamOptions() {
        var flightIds = $("#flightIds").val();
        return {
            flightIds: flightIds
        }
    }

    //获取地理围栏参数
    function getGeoFence() {
        var geoFence = $("#geoFence").val();
        return JSON.parse(geoFence);
    }

    //更新当前时间界面
    function updateProgress(currentTime) {
        $("#progress").html(currentTime);
    }

    //获取默认地理围栏参数
    function getDefaultGeoFence() {
        return {
            center: [103.9, 30.58],
            radius: 200000,
            unit: "degree"
        }
    }

    //默认设置参数
    function getDefaultControlOptions() {
        var start = "2017-05-03 10:22:31";
        var end = "2017-05-03 17:00:00";
        return {
            startTime: start,
            endTime: end,
            speed: 1000,
            frequency: 100,
        }
    }

    function initMap() {
        if (!map) {
            map = L.map('map', {
                center: [34, 110],
                maxZoom: 20,
                minZoom: 3,
                zoom: 5,
                crs: L.CRS.EPSG3857
            });
            var mbAttr = 'Imagery © <a href="http://mapbox.com">Mapbox</a>',
                token = "pk.eyJ1IjoibWFwYm94IiwiYSI6ImNpejY4NXVycTA2emYycXBndHRqcmZ3N3gifQ.rJcFIG214AriISLbB6B5aw",
                mbUrl = 'https://api.tiles.mapbox.com/v4/{id}/{z}/{x}/{y}.png?access_token=' + token;

            L.tileLayer(mbUrl, {id: 'mapbox.dark', attribution: mbAttr}).addTo(map);
        }

        initTimeControlView();
    }

    //初始化时间控制控件，仅UI
    function initTimeControlView() {

        var control = L.control({position: "topright"});
        control.onAdd = function () {
            var me = this;
            me._div = L.DomUtil.create('div', 'panel panel-primary controlPane');
            me._div.style.width = "300px";
            var titleDiv = $("<div class='panel-heading text-center' id='toggle' style='cursor: pointer'>" +
                "<span class='panel-title text-center'>控制台</span>&nbsp;" +
                "<span class='glyphicon glyphicon-triangle-top' id='toggleIcon' ></span></div>").appendTo(me._div);

            var contentDiv = $("<div class='panel-body content center-block' style='font-size: 14px'></div>").appendTo(me._div);

            var optionsDiv = $("<div class='' id='options'></div>").appendTo(contentDiv);

            $("<div class='form-group'><label class='text-right' for='flightIds'  >" +
                "航班号<span style='color:red'>*</span></label>" +
                "<select class='selectpicker ' id='flightIds' multiple data-actions-box='true' title='请选择'>" +
                initSelectOpitons() +
                "</select></div><hr/>").appendTo(optionsDiv);

            function initSelectOpitons() {
                var str = '';
                flightIdens.map(function (ident, key) {
                    if (key !== 0) {
                        str += "<option value=" + ident + ">" + ident + "</option> "
                    } else {
                        str += "<option value=" + ident + " selected>" + ident + "</option> "
                    }

                });
                return str;
            }

            var defaultOption = getDefaultControlOptions();
            var defaultGeoFence = getDefaultGeoFence();
            $("<div class='form-group form-inline'><label class='text-right' for='geoFence' >地理围栏</label>" +
                "<input id='geoFence' type='text' class='form-control input-sm' placeholder='" + JSON.stringify(defaultGeoFence) +
                "' value='" + JSON.stringify(defaultGeoFence) + "'/></div></div><hr/>").appendTo(optionsDiv);

            $("<div class='form-group form-inline'><label class='text-right' for='startTime' >起始时间<span style='color:red'>*</span></label>" +
                "<input id='startTime' type='text' class='form-control input-sm' placeholder='" + defaultOption.startTime +
                "' value='" + defaultOption.startTime + "'/></div></div>").appendTo(optionsDiv);

            $("<div class='form-group form-inline'><label class='text-right' for='endTime' >终止时间<span style='color:red'>*</span></label>" +
                "<input id='endTime' type='text' class='form-control input-sm' placeholder='" + defaultOption.endTime +
                "' value='" + defaultOption.endTime + "'/></div></div>").appendTo(optionsDiv);

            $("<div class='form-group form-inline'><label class='text-right' for='speed' >刷新步长(ms)</label>" +
                "<input id='speed' type='number' min='1' class='form-control input-sm' placeholder='" + defaultOption.speed +
                "' value='" + defaultOption.speed + "'/></div></div>").appendTo(optionsDiv);

            $("<div class='form-group form-inline'><label class='text-right' for='frequency' >刷新频率(ms)</label>" +
                "<input id='frequency' type='number' min='1' class='form-control input-sm' placeholder='" + defaultOption.frequency +
                "' value='" + defaultOption.frequency + "'/></div></div>").appendTo(optionsDiv);

            var progressDiv = $("<div class='form-group'><div class='form-horizontal text-center'><div class='form-group'>" +
                "<label  for='progress'>当前时间：</label><span class='form-control-static' id='progress'>未开始</span>" +
                "</div></div></div>").appendTo(contentDiv);

            var controlDiv = $("<section><div class='form-inline text-center'>" +
                "<input id='start' type='button'  class='btn btn-default text-center' value='开始'/>&nbsp;" +
                "<input id='pause' type='button'  class='btn btn-default text-center' value='暂停'/>&nbsp;" +
                "<input id='stop' type='button'  class='btn btn-default text-center' value='停止'/>" +
                "</div></section>").appendTo(contentDiv);


            me._div.addEventListener('mouseover', function () {
                me._map.dragging.disable();
                me._map.scrollWheelZoom.disable();
                me._map.doubleClickZoom.disable();
            });
            me._div.addEventListener('mouseout', function () {
                me._map.dragging.enable();
                me._map.scrollWheelZoom.enable();
                me._map.doubleClickZoom.enable();
            });

            return me._div;
        };
        control.addTo(map);
        var dateOptions = {
            format: "YYYY-MM-DD HH:mm:ss",
            stepping: 1,
            showClose: true,
            locale: 'zh-cn'
        };
        $("#startTime").datetimepicker(dateOptions);
        $("#endTime").datetimepicker(dateOptions);
        $('#flightIds').selectpicker();
        $("#start").on('click', function () {
            $("#options").slideUp("fast", function () {
                toggle(this);
            });
            start();
        });
        $("#pause").on('click', pause);
        $("#stop").on('click', stop);
        $("#toggle").on('click', function () {
            $("#options").slideToggle("fast", function () {
                toggle(this);
            });
            return false;
        });

        function toggle(ele) {
            if ($(ele).is(":visible")) {
                $("#toggleIcon").attr('class', "glyphicon glyphicon-triangle-top");
            } else {
                $("#toggleIcon").attr('class', "glyphicon glyphicon-triangle-bottom");
            }
        }
    }

    function clearAll() {
        if (timeControl) {
            timeControl.destroy();
            timeControl = null;
        }
        if (trackRenderer) {
            map.removeLayer(trackRenderer);
            trackRenderer = null;
        }
        if (liveRenderer) {
            map.removeLayer(liveRenderer);
            liveRenderer = null;
        }
        if (liveDataSet) {
            liveDataSet = null;
        }
        if (planeLabels) {
            for (var item in planeLabels) {
                planeLabels[item].remove();
            }
            planeLabels = {};
        }
        if (geoFenceLayer) {
            geoFenceLayer.remove();
            geoFenceLayer = null;
        }
    }

</script>
</body>
</html>


